package inabit.utils;

import inabit.data.FireableRule;
import inabit.data.Rule;
import inabit.data.expression.ConsequentExpression;
import inabit.data.expression.Expression;
import inabit.data.expression.LogicExpression;

import java.util.ArrayList;
import java.util.List;
/**Break the normal rule to a list of fireable rules, which consequent expression
 * can be executed during inference process
 * @author Rafael Rocha*/
public class RuleUtils{
	public static List<FireableRule> getNormalizedRules(Rule rule) {
		List<FireableRule> finalRules = new ArrayList<FireableRule>();
		List<FireableRule> rules = new ArrayList<FireableRule>();
		rules.addAll(normalizeConsequent(rule));
		
		for(FireableRule r : rules) {
			List<Expression> expressions = normalizePredicate(r.getPrecedent());
			for(Expression expression : expressions) {
				finalRules.add(new FireableRule(r.getID(), expression, (ConsequentExpression)r.getConsequent()));
			}
		}
		
		return rules;
	}
	
	private static List<Expression> normalizePredicate(Expression exp) {
		List<Expression> expressions = new ArrayList<Expression>();
		
		if(!(exp instanceof LogicExpression)) {
			expressions.add(exp);
			return expressions;
		}
		LogicExpression logExp = (LogicExpression)exp;
		List<Expression> leftSide = normalizePredicate(logExp.getLeftSide());
		List<Expression> rightSide = normalizePredicate(logExp.getRightSide());
		
		for(Expression leftNode : leftSide) {
			for(Expression rightNode : rightSide) {
				Expression left = leftNode.clone();
				Expression right = rightNode.clone();
				
				if(exp.hasNot()) {
					left = left.negation();
					right = right.negation();
					if(logExp.getOperator() == LogicExpression.AND) {
						expressions.add(left);
						expressions.add(right);
					}else if(logExp.getOperator() == LogicExpression.OR) {
						expressions.add(new LogicExpression(left, LogicExpression.AND, right));
					}
				}else {
					if(logExp.getOperator() == LogicExpression.AND) {
						expressions.add(new LogicExpression(left, LogicExpression.AND, right));
					}else if(logExp.getOperator() == LogicExpression.OR) {
						expressions.add(left);
						expressions.add(right);
					}
				}
			}
		}
		return expressions;
	}
	
	private static List<FireableRule> normalizeConsequent(Rule rule) {
		List<FireableRule> rules = new ArrayList<FireableRule>();
		
		Expression consequent = rule.getConsequent();
		
		if(consequent instanceof ConsequentExpression) {
			FireableRule simpleRule = new FireableRule(rule.getID(), rule.getPrecedent(), (ConsequentExpression)rule.getConsequent());
			rules.add(simpleRule);
			return rules;
		}else if(!(consequent instanceof LogicExpression)) {//There are no sensor in consequent
			return rules;
		}
		LogicExpression logExp = (LogicExpression)consequent;

		
		if(logExp.hasNot()) {
			Rule r1 = new Rule(rule.getID(), rule.getPrecedent().clone(), logExp.getLeftSide().negation());
			Rule r2 = new Rule(rule.getID(), rule.getPrecedent().clone(), logExp.getRightSide().negation());
			
			
			if(logExp.getOperator() == LogicExpression.AND) {
				Expression leftExpr = logExp.getLeftSide().clone();
				Expression rightExpr = logExp.getRightSide().clone();
				r1 = new Rule(r1.getID(), new LogicExpression(
						r1.getPrecedent(),
						LogicExpression.AND,
						rightExpr),
						r1.getConsequent());
				r2 = new Rule(r1.getID(), new LogicExpression(
						r1.getPrecedent(),
						LogicExpression.AND,
						leftExpr),
						r1.getConsequent());
			}
			
			rules.addAll(normalizeConsequent(r1));
			rules.addAll(normalizeConsequent(r2));
		}else {
			Rule r1 = new Rule(rule.getID(), rule.getPrecedent().clone(), logExp.getLeftSide());
			Rule r2 = new Rule(rule.getID(), rule.getPrecedent().clone(), logExp.getRightSide());
			
			if(logExp.getOperator() == LogicExpression.OR) {
				r1 = new Rule(
						r1.getID(),
						new LogicExpression(
								r1.getPrecedent(),
								LogicExpression.AND,
								(logExp.getRightSide().negation())),r1.getConsequent());
				
				r2 = new Rule(
						r2.getID(),
						new LogicExpression(
								r2.getPrecedent(),
								LogicExpression.AND,
								logExp.getLeftSide().negation()),r2.getConsequent());
			}
			rules.addAll(normalizeConsequent(r1));
			rules.addAll(normalizeConsequent(r2));
		}
		return rules;
	}
}
